// PLUMBER - Easy baseline grids with SASS
// https://jamonserrano.github.io/plumber-sass
// Copyright 2016 Viktor Honti
// MIT License

/// Create better looking documents and speed up CSS development by adding vertical rhythm to your page.
/// @param {Number} $baseline [null] - Baseline ratio (Fraction between 0 and 1)
/// @param {Number} $font-size [null] - Font size as a fraction of grid height (Positive number)
/// @param {String} $grid-height [null] - Grid height (Any unit)
/// @param {Number} $leading-top [null] - Top leading* as a multiple of grid height (Integer)
/// @param {Number} $leading-bottom [null] - Bottom leading* as a multiple of grid height (Integer)
/// @param {Number} $line-height [null] - Line height as a multiple of grid height (Positive integer)
/// @param {Boolean} $use-baseline-origin [null] - Set the origin of leadings to the baseline
/// @example
/// //SCSS
/// p {
///     @include plumber(
///         $grid-height: 1rem,
///         $baseline: 0.158203,
///         $font-size: 1.75,
///         $line-height: 3,
///         $leading-top: 1,
///         $leading-bottom: 2
///     );
///     font-family: Roboto, sans-serif;
/// }
/// Output CSS
/// p {
///     font-size: 1.75rem;
///     line-height: 3rem;
///     margin-top: 0;
///     padding-top: 0.81641rem;
///     padding-bottom: 0.18359rem;
///     margin-bottom: 2rem;
///     font-family: Roboto, sans-serif;
/// }
/// @link https://jamonserrano.github.io/plumber-sass/
/// @author jamonserrano:https://github.com/jamonserrano
@mixin plumber(
  $font-size: null,
  $line-height: null,
  $leading-top: null,
  $leading-bottom: null,
  $grid-height: null,
  $baseline: null,
  $use-baseline-origin: null
) {
  // *** VALIDATE PARAMETERS ***
  // font-size
  @if not $font-size {
    $font-size: -plumber-get-default(font-size);
  }
  @if not unitless($font-size) or $font-size <= 0 {
    @error '$font-size parameter must be a positive unitless number, got #{$font-size} instead';
  }

  // line-height
  @if not $line-height {
    $line-height: -plumber-get-default(line-height);
  }
  @if not
    unitless($line-height) or
    $line-height !=
    round($line-height) or
    $line-height <
    1
  {
    @error '$line-height parameter must be a positive unitless integer, got #{$line-height} instead';
  }

  // leading-top
  @if not $leading-top {
    $leading-top: -plumber-get-default(leading-top);
  }
  @if not -plumber-is-integer($leading-top) {
    @error '$leading-top parameter must be a non-negative integer, got #{$leading-top} instead.';
  }

  // leading-bottom
  @if not $leading-bottom {
    $leading-bottom: -plumber-get-default(leading-bottom);
  }
  @if not -plumber-is-integer($leading-bottom) {
    @error '$leading-bottom parameter must be a non-negative integer, got #{$leading-bottom} instead';
  }

  // grid-height
  @if not $grid-height {
    $grid-height: -plumber-get-default(grid-height);
  }
  @if unitless($grid-height) or $grid-height < 0 {
    @error '$grid-height parameter must be a positive unit, got #{$grid-height} instead';
  }

  // baseline
  @if not $baseline {
    $baseline: -plumber-get-default(baseline);
  }
  @if not $baseline {
    @error '$baseline must be passed as a parameter or defined in defaults';
  } @else if not (unitless($baseline) and $baseline >= 0 and $baseline < 1) {
    @error '$baseline parameter must be a unitless fraction between 0 and 1, got #{$baseline} instead';
  }

  // use-baseline-origin
  @if not $use-baseline-origin {
    $use-baseline-origin: -plumber-get-default(use-baseline-origin);
  }
  @if type-of($use-baseline-origin) != bool {
    @error '$use-baseline-origin parameter must be Boolean, got #{$use-baseline-origin} instead';
  }

  // *** CALCULATE BASELINE CORRECTION ***
  // the distance of the original baseline from the bottom
  $baseline-from-bottom: ($line-height - $font-size) / 2 +
    ($font-size * $baseline);
  // the corrected baseline will be on the nearest gridline
  $corrected-baseline: round($baseline-from-bottom);
  // the difference between the original and the corrected baseline
  $baseline-difference: $corrected-baseline - $baseline-from-bottom;

  // if baseline origin is used for leadings substract the distance of the baseline from the edges
  @if $use-baseline-origin == true {
    $leading-top: $leading-top - ($line-height - $corrected-baseline);
    $leading-bottom: $leading-bottom - $corrected-baseline;
  }

  // *** CALCULATE FONT SIZE AND LINE HEIGHT
  $font-size: $font-size * $grid-height;
  $line-height: $line-height * $grid-height;

  // *** CALCULATE MARGINS AND PADDINGS ***
  $padding-top: null;
  $margin-top: null;
  $margin-bottom: null;
  $padding-bottom: null;

  @if $baseline-difference < 0 {
    // add top leading
    $margin-top: $leading-top * $grid-height;
    // push the baseline down to the next gridline
    $padding-top: -$baseline-difference * $grid-height;
    // add the remaining distance to reach the next gridline
    $padding-bottom: (1 + $baseline-difference) * $grid-height;
    // add bottom leading and remove the 1 excess grid height that comes from pushing down
    $margin-bottom: ($leading-bottom - 1) * $grid-height;
  } @else {
    // add top leading and remove the 1 excess grid height that comes from pulling up
    $margin-top: ($leading-top - 1) * $grid-height;
    // pull the baseline up to the previous gridline
    $padding-top: (1 - $baseline-difference) * $grid-height;
    // add the remaining distance to reach the next gridline
    $padding-bottom: $baseline-difference * $grid-height;
    // add bottom leading
    $margin-bottom: $leading-bottom * $grid-height;
  }

  // round pixel values to decrease browser inconsistencies
  @if unit($grid-height) == 'px' {
    $line-height: -plumber-round($line-height);
    $margin-top: -plumber-round($margin-top);
    $padding-top: -plumber-round($padding-top);
    $padding-bottom: -plumber-round($padding-bottom);
    $margin-bottom: -plumber-round($margin-bottom);
  }

  // *** CSS OUTPUT ***
  font-size: $font-size;
  line-height: $line-height;
  margin-top: $margin-top;
  padding-top: $padding-top;
  padding-bottom: $padding-bottom;
  margin-bottom: $margin-bottom;
}

// *** DEFAULTS ***
// Do not change it here, use the plumber-set-defaults mixin instead!
$-plumber-defaults: (
  font-size: 2,
  line-height: 3,
  leading-top: 0,
  leading-bottom: 0,
  grid-height: 1rem,
  baseline: null,
  use-baseline-origin: false,
) !default;

/// Merge provided settings into the defaults map
@mixin plumber-set-defaults($defaults...) {
  $-plumber-defaults: map-merge(
    $-plumber-defaults,
    keywords($defaults)
  ) !global;
}

/// Get a default value
@function -plumber-get-default($key) {
  @return map-get($-plumber-defaults, $key);
}

/// Check if a value is a non-negative integer
@function -plumber-is-integer($value) {
  @return (unitless($value) and $value == round($value));
}

/// Round value to the nearest quarter pixel
/// This provides reasonable precision and prevents grid creep (by fractions adding up) in most browsers
@function -plumber-round($value) {
  @return round($value * 4) / 4;
}
